package cn.funeralobjects.upms.service.impl;

import cn.funeralobjects.common.model.KeyValue;
import cn.funeralobjects.common.repository.EnableRepository;
import cn.funeralobjects.common.repository.EntityRepository;
import cn.funeralobjects.common.service.annotataion.CommonRollback;
import cn.funeralobjects.common.service.annotataion.CommonService;
import cn.funeralobjects.upms.entity.EApi;
import cn.funeralobjects.upms.entity.QEApi;
import cn.funeralobjects.upms.enums.HttpMethod;
import cn.funeralobjects.upms.enums.Protocol;
import cn.funeralobjects.upms.model.Api;
import cn.funeralobjects.upms.repository.ApiRepository;
import cn.funeralobjects.upms.service.ApiService;
import cn.funeralobjects.util.Assert;
import cn.funeralobjects.util.CollectionUtils;
import cn.funeralobjects.util.annotation.*;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.JPQLQueryFactory;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ApiService的实现类
 *
 * @author FuneralObjects
 * Create date: 2020/4/26 11:35 AM
 */
@CommonService
public class ApiServiceImpl implements ApiService {

    @Resource
    private ApiRepository apiRepository;

    @Resource
    private JPQLQueryFactory jpqlQueryFactory;

    @Override
    public EnableRepository<EApi, Integer> enableRepository() {
        return apiRepository;
    }

    @Override
    public EntityRepository<EApi, Integer> entityRepository() {
        return apiRepository;
    }

    @CommonRollback
    @AssertArg
    @Override
    public EApi addApi(@ArgHasLength String scope, String name, @ArgNonNull Protocol protocol, @ArgNonNull HttpMethod method, @ArgHasLength String url) {
        notConflict(scope, protocol, method, url);
        return apiRepository.save(new EApi().setMethod(method).setName(name).setProtocol(protocol).setScope(scope).setUrl(url));
    }

    @AssertArg
    @Override
    public Optional<EApi> findApi(@ArgHasLength String scope, @ArgNonNull Protocol protocol, @ArgNonNull HttpMethod method, @ArgHasLength String url) {
        return apiRepository.findAllByScopeAndProtocolAndMethodAndUrl(scope, protocol, method, url);
    }

    @CommonRollback
    @AssertArg
    @Override
    public List<EApi> addOrUpdateAll(@ArgNotEmpty @ArgNoNullElements List<Api> apiDto) {
        Map<Api, String> apiNameMap = apiDto.stream().collect(Collectors.toMap(api -> api, Api::getName));
        //过滤出需要更新名称的api
        List<EApi> updates = filterForExistsApi(apiDto)
                .stream()
                .map(api -> new KeyValue<>(new Api(api.getName(), api.getScope(), api.getProtocol(), api.getMethod(), api.getUrl()), api))
                .filter(keyValue -> {
                    String newName = apiNameMap.get(keyValue.getKey());
                    String oldName = keyValue.getValue().getName();
                    //移除已存在的api数据
                    apiNameMap.remove(keyValue.getKey());
                    //若相同，则无需更新名称
                    if (Objects.equals(newName, oldName)) {
                        return false;
                    }
                    //更新名称
                    keyValue.getValue().setName(newName);
                    return true;
                }).map(KeyValue::getValue)
                .collect(Collectors.toList());
        //更新api名称
//        if (CollectionUtils.notEmpty(updates)) {
//            updateName(updates.stream().collect(Collectors.toMap(EApi::getId, EApi::getName)));
//        }
        //新增api
        List<EApi> saves = Optional.of(apiNameMap)
                .filter(CollectionUtils::notEmpty)
                .map(Map::keySet)
                .map(es -> es.stream().map(api -> new EApi()
                        .setName(api.getName())
                        .setMethod(api.getMethod())
                        .setProtocol(api.getProtocol())
                        .setScope(api.getScope())
                        .setUrl(api.getUrl())
                ).collect(Collectors.toList()))
                .map(list -> apiRepository.saveAll(list))
                .orElseGet(() -> new ArrayList<>(0));
        return List.of(updates, saves).stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }

    @CommonRollback
    @Override
    public void updateName(@ArgNotEmpty Map<Integer, String> idNameMap) {
        idNameMap.forEach((id, name) -> {
            validateId(id);
            Assert.argHasLength(name, "name");
        });
        idNameMap.entrySet()
                .parallelStream()
                .forEach(e -> apiRepository.updateNameById(e.getValue(), e.getKey()));
    }

    @CommonRollback
    @Override
    public void updateName(@ArgNotZero Integer id, @ArgHasLength String name) {
        apiRepository.updateNameById(name, id);
    }

    @AssertArg
    @Override
    public boolean exists(@ArgHasLength String scope, @ArgNonNull Protocol protocol, @ArgNonNull HttpMethod method, @ArgHasLength String url) {
        return apiRepository.existsAllByScopeAndProtocolAndMethodAndUrl(scope, protocol, method, url);
    }

    @AssertArg
    @Override
    public List<EApi> filterForExistsApi(@ArgNotEmpty @ArgNoNullElements Collection<Api> apiDto) {
        JPQLQuery<EApi> query = jpqlQueryFactory.selectFrom(QEApi.eApi);
        apiDto.stream().map(api -> QEApi.eApi.scope.eq(api.getScope())
                .and(QEApi.eApi.method.eq(api.getMethod()))
                .and(QEApi.eApi.protocol.eq(api.getProtocol()))
                .and(QEApi.eApi.url.eq(api.getUrl()))).reduce(BooleanExpression::or).ifPresent(query::where);
        return query.fetch();
    }
}
